import { AppFileSelectConfigType } from '@/types/app/type';
import { FlowNodeInputItemType } from '@/types/workflow/io';

export enum ChatStatusEnum {
  loading = 'loading',
  running = 'running',
  finish = 'finish',
}

export enum AppTypeEnum {
  folder = 'folder',
  simple = 'simple',
  workflow = 'advanced',
  plugin = 'plugin',
  httpPlugin = 'httpPlugin',
}

export type AppListItemType = {
  _id: string;
  tmbId: string;
  name: string;
  avatar: string;
  intro: string;
  type: AppTypeEnum;
  updateTime: Date;
};

export type InitChatResponse = {
  chatId?: string;
  appId: string;
  userAvatar?: string;
  title: string;
  variables: Record<string, any>;
  history: any[];
  app: {
    chatConfig?: any;
    chatModels?: string[];
    name: string;
    avatar: string;
    intro: string;
    canUse?: boolean;
    type: `${AppTypeEnum}`;
    pluginInputs: FlowNodeInputItemType[];
  };
};

export const defaultChatData: InitChatResponse = {
  chatId: '',
  appId: '',
  app: {
    name: 'Loading',
    avatar: '/icon/logo.svg',
    intro: '',
    canUse: false,
    type: AppTypeEnum.simple,
    pluginInputs: [],
  },
  title: '新对话',
  variables: {},
  history: [],
};

export type DeleteChatItemProps = {
  appId: string;
  chatId: string;
  contentId?: string;
};

type ShareChatAuthProps = {
  shareId?: string;
  outLinkUid?: string;
};
type TeamChatAuthProps = {
  teamId?: string;
  teamToken?: string;
};
export type OutLinkChatAuthProps = ShareChatAuthProps & TeamChatAuthProps;

export type UpdateHistoryProps = OutLinkChatAuthProps & {
  appId: string;
  chatId: string;
  title?: string;
  customTitle?: string;
  top?: boolean;
};

export type DelHistoryProps = OutLinkChatAuthProps & {
  appId: string;
  chatId: string;
};

export type ClearHistoriesProps = OutLinkChatAuthProps & {
  appId?: string;
};

export type HistoryItemType = {
  chatId: string;
  updateTime: Date;
  customTitle?: string;
  title: string;
};

export type ChatHistoryItemType = HistoryItemType & {
  appId: string;
  top: boolean;
};

export enum ChatItemValueTypeEnum {
  text = 'text',
  file = 'file',
  tool = 'tool',
}

export type ToolModuleResponseItemType = {
  id: string;
  toolName: string; // tool name
  toolAvatar: string;
  params: string; // tool params
  response: string;
  functionName: string;
};

export type AIChatItemValueItemType = {
  type: ChatItemValueTypeEnum.text | ChatItemValueTypeEnum.tool;
  text?: {
    content: string;
  };
  tools?: ToolModuleResponseItemType[];
};

export enum ChatRoleEnum {
  System = 'System',
  Human = 'Human',
  AI = 'AI',
}
export enum ChatFileTypeEnum {
  image = 'image',
  file = 'file',
}
export type UserChatItemValueItemType = {
  type: ChatItemValueTypeEnum.text | ChatItemValueTypeEnum.file;
  text?: {
    content: string;
  };
  file?: {
    type: `${ChatFileTypeEnum}`;
    name?: string;
    url: string;
  };
};
export type UserChatItemType = {
  obj: ChatRoleEnum.Human;
  value: UserChatItemValueItemType[];
};
export type SystemChatItemValueItemType = {
  type: ChatItemValueTypeEnum.text;
  text?: {
    content: string;
  };
};
export type SystemChatItemType = {
  obj: ChatRoleEnum.System;
  value: SystemChatItemValueItemType[];
};
export type AdminFbkType = {
  dataId: string;
  datasetId: string;
  collectionId: string;
  q: string;
  a?: string;
};
export enum DispatchNodeResponseKeyEnum {
  skipHandleId = 'skipHandleId', // skip handle id
  nodeResponse = 'responseData', // run node response
  nodeDispatchUsages = 'nodeDispatchUsages', // the node bill.
  childrenResponses = 'childrenResponses', // Some nodes make recursive calls that need to be returned
  toolResponses = 'toolResponses', // The result is passed back to the tool node for use
  assistantResponses = 'assistantResponses', // assistant response

  rewriteHistories = 'rewriteHistories', // If have the response, workflow histories will be rewrite

  interactive = 'INTERACTIVE', // is interactive
  runTimes = 'runTimes', // run times
  newVariables = 'newVariables', // new variables
}
export enum FlowNodeTypeEnum {
  emptyNode = 'emptyNode',
  systemConfig = 'userGuide',
  globalVariable = 'globalVariable',
  workflowStart = 'workflowStart',
  chatNode = 'chatNode',

  datasetSearchNode = 'datasetSearchNode',
  datasetConcatNode = 'datasetConcatNode',

  answerNode = 'answerNode',
  classifyQuestion = 'classifyQuestion',
  contentExtract = 'contentExtract',
  httpRequest468 = 'httpRequest468',
  runApp = 'app',
  pluginModule = 'pluginModule',
  pluginInput = 'pluginInput',
  pluginOutput = 'pluginOutput',
  queryExtension = 'cfr',
  tools = 'tools',
  stopTool = 'stopTool',
  lafModule = 'lafModule',
  ifElseNode = 'ifElseNode',
  variableUpdate = 'variableUpdate',
  code = 'code',
  textEditor = 'textEditor',
  customFeedback = 'customFeedback',
}
export interface scoreItem {
  type: string;
  value: number;
  index: number;
}
export interface Quote {
  sourceName: string;
  sourceId: string;
  datasetId: string;
  collectionId: string;
  q: string;
  id: string;
  score: scoreItem[];
  s: number;
}
export type ChatHistoryItemResType = {
  nodeId: string;
  moduleType: FlowNodeTypeEnum;
  moduleName: string;
  quoteList: Quote[];
};
export type AIChatItemType = {
  obj: ChatRoleEnum.AI;
  value: AIChatItemValueItemType[];
  userGoodFeedback?: string;
  userBadFeedback?: string;
  customFeedbacks?: string[];
  adminFeedback?: AdminFbkType;
  [DispatchNodeResponseKeyEnum.nodeResponse]?: ChatHistoryItemResType[];
};
export type UserInputFileItemType = {
  id: string;
  rawFile?: File;
  type: `${ChatFileTypeEnum}`;
  name: string;
  icon: string; // img is base64
  url?: string;
};
export type ChatBoxInputType = {
  text?: string;
  files?: UserInputFileItemType[];
};
export type ChatSiteItemType = (UserChatItemType | SystemChatItemType | AIChatItemType) & {
  dataId: string;
  status: `${ChatStatusEnum}`;
  moduleName?: string;
  ttsBuffer?: Uint8Array;
  responseData?: ChatHistoryItemResType[];
} & ChatBoxInputType;

export type ChatItemType = (UserChatItemType | SystemChatItemType | AIChatItemType) & {
  dataId?: string;
};

export type ChatItemValueItemType =
  | UserChatItemValueItemType
  | SystemChatItemValueItemType
  | AIChatItemValueItemType;

export type RuntimeUserPromptType = {
  files: UserChatItemValueItemType['file'][];
  text: string;
};

export enum ChatCompletionRequestMessageRoleEnum {
  'System' = 'system',
  'User' = 'user',
  'Assistant' = 'assistant',
  'Function' = 'function',
  'Tool' = 'tool',
}

export type ChatCompletionMessageParam = {
  dataId?: string;
  role?: `${ChatCompletionRequestMessageRoleEnum}`;
};

export type ChatCompletionMessageFunctionCall = {
  id?: string;
  toolName?: string;
  toolAvatar?: string;
};

export interface ChatCompletionContentPartText {
  /**
   * The text content.
   */
  text: string;

  /**
   * The type of the content part.
   */
  type: 'text';
}

export namespace ChatCompletionContentPartImage {
  export interface ImageURL {
    /**
     * Either a URL of the image or the base64 encoded image data.
     */
    url: string;

    /**
     * Specifies the detail level of the image. Learn more in the
     * [Vision guide](https://platform.openai.com/docs/guides/vision/low-or-high-fidelity-image-understanding).
     */
    detail?: 'auto' | 'low' | 'high';
  }
}

export interface ChatCompletionContentPartImage {
  image_url: ChatCompletionContentPartImage.ImageURL;

  /**
   * The type of the content part.
   */
  type: 'image_url';
}

export type ChatCompletionMessageToolCall = {
  toolName?: string;
  toolAvatar?: string;
};

export type ChatCompletionContentPart =
  | ChatCompletionContentPartText
  | ChatCompletionContentPartImage;

export type ChatCompletionToolMessageParam = { name: string };

export interface ChatCompletionFunctionMessageParam {
  /**
   * The contents of the function message.
   */
  content: string | null;

  /**
   * The name of the function to call.
   */
  name: string;

  /**
   * The role of the messages author, in this case `function`.
   */
  role: 'function';
}

export enum OAuthEnum {
  github = 'github',
  google = 'google',
  wechat = 'wechat',
}

export type LLMModelItemType = {
  model: string;
  name: string;
  avatar?: string;
  maxContext: number;
  maxResponse: number;
  quoteMaxToken: number;
  maxTemperature: number;

  charsPointsPrice: number; // 1k chars=n points

  censor?: boolean;
  vision?: boolean;

  // 是否设置为知识库处理模型（QA）
  datasetProcess?: boolean; // dataset
  // 是否用于问题分类
  usedInClassify?: boolean; // classify
  // 是否用于内容提取
  usedInExtractFields?: boolean; // extract fields
  // 是否用于工具调用
  usedInToolCall?: boolean; // tool call
  // 是否用于问题优化
  usedInQueryExtension?: boolean; // query extension

  functionCall: boolean;
  toolChoice: boolean;

  customCQPrompt: string;
  customExtractPrompt: string;

  defaultSystemChatPrompt?: string;
  defaultConfig?: Record<string, any>;

  isLocal?: boolean;
  isActive?: boolean;
  isCustom?: boolean;
  isRecommend?: boolean;
  isDefault?: boolean;
  // 测试通过
  isConnect?: boolean;
};

export type VectorModelItemType = {
  model: string; // model name
  name: string; // show name
  avatar?: string;
  defaultToken: number; // split text default token
  charsPointsPrice: number; // 1k tokens=n points
  maxToken: number; // model max token
  weight: number; // training weight
  hidden?: boolean; // Disallow creation
  defaultConfig?: Record<string, any>; // post request config
  dbConfig?: Record<string, any>; // Custom parameters for storage
  queryConfig?: Record<string, any>; // Custom parameters for query
};

export type ReRankModelItemType = {
  model: string;
  name: string;
  charsPointsPrice: number;
  requestUrl: string;
  requestAuth: string;
};

export type AudioSpeechModelType = {
  model: string;
  name: string;
  charsPointsPrice: number;
  voices: { label: string; value: string; bufferId: string }[];
};

export type WhisperModelType = {
  model: string;
  name: string;
  charsPointsPrice: number; // 60s = n points
};

export type InitDateResponse = {
  llmModels: LLMModelItemType[];
  vectorModels: VectorModelItemType[];
  reRankModels: ReRankModelItemType[];
  whisperModel: WhisperModelType;
  systemVersion: string;
  audioSpeechModels: AudioSpeechModelType[];
  feConfigs: FastGPTFeConfigsType;
  subPlans?: SubPlanType;
};

export type FastGPTFeConfigsType = {
  show_emptyChat?: boolean;
  register_method?: ['email' | 'phone'];
  login_method?: ['email' | 'phone']; // Attention: login method is diffrent with oauth
  find_password_method?: ['email' | 'phone'];
  bind_notification_method?: ['email' | 'phone'];
  show_appStore?: boolean;
  show_git?: boolean;
  show_pay?: boolean;
  show_openai_account?: boolean;
  show_promotion?: boolean;
  show_team_chat?: boolean;
  show_compliance_copywriting?: boolean;
  concatMd?: string;

  docUrl?: string;
  chatbotUrl?: string;
  openAPIDocUrl?: string;
  systemPluginCourseUrl?: string;
  appTemplateCourse?: string;

  systemTitle?: string;
  systemDescription?: string;
  googleClientVerKey?: string;
  isPlus?: boolean;
  sso?: {
    icon?: string;
    title?: string;
    url?: string;
  };
  oauth?: {
    github?: string;
    google?: string;
    wechat?: string;
  };
  limit?: {
    exportDatasetLimitMinutes?: number;
    websiteSyncLimitMinuted?: number;
  };
  scripts?: { [key: string]: string }[];
  favicon?: string;
  customApiDomain?: string;
  customSharePageDomain?: string;

  uploadFileMaxAmount?: number;
  uploadFileMaxSize?: number;
  lafEnv?: string;
};

export enum SubTypeEnum {
  standard = 'standard',
  extraDatasetSize = 'extraDatasetSize',
  extraPoints = 'extraPoints',
}

export enum StandardSubLevelEnum {
  free = 'free',
  experience = 'experience',
  team = 'team',
  enterprise = 'enterprise',
  custom = 'custom',
}
export type TeamStandardSubPlanItemType = {
  price: number; // read price / month
  pointPrice: number; // read price/ one thousand
  totalPoints: number; // n
  maxTeamMember: number;
  maxAppAmount: number; // max app or plugin amount
  maxDatasetAmount: number;
  chatHistoryStoreDuration: number; // n day
  maxDatasetSize: number;
  trainingWeight: number; // 1~4
  permissionCustomApiKey: boolean;
  permissionCustomCopyright: boolean; // feature
  permissionWebsiteSync: boolean;
  permissionReRank: boolean;
};
export type StandSubPlanLevelMapType = Record<
  `${StandardSubLevelEnum}`,
  TeamStandardSubPlanItemType
>;

export type SubPlanType = {
  [SubTypeEnum.standard]: StandSubPlanLevelMapType;
  [SubTypeEnum.extraDatasetSize]: {
    price: number;
  };
  [SubTypeEnum.extraPoints]: {
    price: number;
  };
};

export type AdminMarkType = {
  dataId?: string;
  datasetId?: string;
  collectionId?: string;
  q: string;
  a?: string;
};

export type ComponentRef = {
  restartChat: () => void;
  scrollToBottom: (behavior?: 'smooth' | 'auto') => void;
  sendPrompt: (question: string) => void;
};

export type ChatBoxInputFormType = {
  input: string;
  files: UserInputFileItemType[];
  chatStarted: boolean;
};

export type StartChatFnProps = {
  messages: ChatCompletionMessageParam[];
  responseChatItemId?: string;
  controller: AbortController;
  variables: Record<string, any>;
  chatId: string;
  generatingMessage: (e: generatingMessageProps) => void;
};

export type generatingMessageProps = {
  event: any;
  text?: string;
  name?: string;
  status?: 'running' | 'finish';
  tool?: ToolModuleResponseItemType;
  variables?: Record<string, any>;
};

export enum SseResponseEventEnum {
  error = 'error',
  answer = 'answer', // animation stream
  fastAnswer = 'fastAnswer', // direct answer text, not animation
  flowNodeStatus = 'flowNodeStatus', // update node status

  toolCall = 'toolCall', // tool start
  toolParams = 'toolParams', // tool params return
  toolResponse = 'toolResponse', // tool response return
  flowResponses = 'flowResponses', // sse response request
  updateVariables = 'updateVariables',
  contentViolation = 'contentViolation',
  interactive = 'interactive', // user select
}

export enum VariableInputEnum {
  input = 'input',
  textarea = 'textarea',
  select = 'select',
  custom = 'custom',
}

export enum TTSTypeEnum {
  none = 'none',
  web = 'web',
  model = 'model',
}

export type AppTTSConfigType = {
  type: 'none' | 'web' | 'model';
  model?: string;
  voice?: string;
  speed?: number;
};

export const MessageCardStyle = {
  px: 4,
  py: 3,
  borderRadius: '0 8px 8px 8px',
  boxShadow: 'none',
  display: 'inline-block',
  maxW: ['calc(100% - 25px)', 'calc(100% - 40px)'],
  color: 'myGray.900',
};

export const MARKDOWN_QUOTE_SIGN = 'QUOTE SIGN';

export const textareaMinH = '22px';

export enum MongoImageTypeEnum {
  systemAvatar = 'systemAvatar',
  appAvatar = 'appAvatar',
  pluginAvatar = 'pluginAvatar',
  datasetAvatar = 'datasetAvatar',
  userAvatar = 'userAvatar',
  teamAvatar = 'teamAvatar',

  chatImage = 'chatImage',
  collectionImage = 'collectionImage',
}

export type preUploadImgProps = OutLinkChatAuthProps & {
  type: `${MongoImageTypeEnum}`;

  expiredTime?: Date;
  metadata?: Record<string, any>;
};
export type UploadImgProps = preUploadImgProps & {
  base64Img: string;
};

export enum BucketNameEnum {
  dataset = 'dataset',
}

export type CompressImgProps = {
  maxW?: number;
  maxH?: number;
  maxSize?: number;
};

export const compressBase64Img = ({
  base64Img,
  maxW = 1080,
  maxH = 1080,
  maxSize = 1024 * 500, // 500kb
}: CompressImgProps & {
  base64Img: string;
}) => {
  return new Promise<string>((resolve, reject) => {
    const fileType =
      /^data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,/.exec(base64Img)?.[1] || 'image/jpeg';

    const img = new Image();
    img.src = base64Img;
    img.onload = async () => {
      let { width } = img;
      let { height } = img;

      if (width > height) {
        if (width > maxW) {
          height *= maxW / width;
          width = maxW;
        }
      } else if (height > maxH) {
        width *= maxH / height;
        height = maxH;
      }

      const canvas = document.createElement('canvas');
      canvas.width = width;
      canvas.height = height;
      const ctx = canvas.getContext('2d');

      if (!ctx) {
        return reject('压缩图片异常');
      }

      ctx.drawImage(img, 0, 0, width, height);
      const compressedDataUrl = canvas.toDataURL(fileType, 1);
      // 移除 canvas 元素
      canvas.remove();

      if (compressedDataUrl.length > maxSize) {
        return reject('图片太大了');
      }

      resolve(compressedDataUrl);
    };
    img.onerror = reject;
  });
};

export const LOGO_ICON = `/assets/ic-logo.svg`;

export const defaultTTSConfig: AppTTSConfigType = { type: 'web' };

export type AppWhisperConfigType = {
  open: boolean;
  autoSend: boolean;
  autoTTSResponse: boolean;
};

export const defaultWhisperConfig: AppWhisperConfigType = {
  open: false,
  autoSend: false,
  autoTTSResponse: false,
};

export const defaultChatInputGuideConfig = {
  open: false,
  textList: [],
  customUrl: '',
};

export const defaultHotQuestionConfig = {
  open: false,
};

export const defaultAppSelectFileConfig: AppFileSelectConfigType = {
  canSelectFile: false,
  canSelectImg: false,
  maxFiles: 10,
};

export type SendPromptFnType = (
  e: ChatBoxInputType & {
    autoTTSResponse?: boolean;
    history?: ChatSiteItemType[];
  },
) => void;
